#pragma once

#include <iostream>
#include <cstdio>
#include <string>
#include <fstream>
#include <sstream>
#include <memory>
#include <filesystem>
#include <unistd.h>
#include <time.h>
#include <mutex>
#include <sys/types.h>

// 获取时间 格式 年-月-日 时-分-秒
std::string CurrentTime()
{
    // 获取时间搓
    time_t _time = time(nullptr);

    // 转化为具体时间
    struct tm curr;
    localtime_r(&_time, &curr);

    // 转化为字符串
    char buff[1024];
    snprintf(buff, sizeof(buff), "%d-%d-%d %d-%d-%d",
             curr.tm_year + 1900, // 从1900开始算
             curr.tm_mon + 1,     // 从0开始算
             curr.tm_mday,
             curr.tm_hour,
             curr.tm_min,
             curr.tm_sec);

    return buff;
}

// 日志文件的默认路径和文件名
const std::string defaultlogpath = "./";
const std::string defaultlogname = "log.txt";

// 日志等级
enum class LogLevel
{
    DEBUG = 1,
    INFO,
    WARNING,
    ERROR,
    FATAL
};
// 控制是否打印DEBUG等级的日志
#define DISABLEDEBUG 0

// 映射日志等级
std::string Level2String(LogLevel level)
{
    switch (level)
    {
    case LogLevel::DEBUG:
        return "DEBUG";
    case LogLevel::INFO:
        return "INFO";
    case LogLevel::WARNING:
        return "WARNIGN";
    case LogLevel::ERROR:
        return "ERROR";
    case LogLevel::FATAL:
        return "FATAL";
    default:
        break;
    }

    return "None";
}

// 刷新策略.
class LogStrategy
{
public:
    virtual ~LogStrategy() = default;
    virtual void SyncLog(const std::string &message) = 0; // 纯虚函数
};

// 输出到屏幕策略
class ConsoleLogStrategy : public LogStrategy
{
public:
    ConsoleLogStrategy()
    {
    }
    ~ConsoleLogStrategy()
    {
    }
    void SyncLog(const std::string &message)
    {
        // 加锁 -- 防止乱序
        std::lock_guard<std::mutex> _lock(_mutex);
        std::cout << message << std::endl;
    }

private:
    std::mutex _mutex;
};

// 文件级(磁盘)策略
class FileLogStrategy : public LogStrategy
{
public:
    FileLogStrategy(const std::string &logpath = defaultlogpath, const std::string &logname = defaultlogname)
        : _logpath(logpath),
          _logname(logname)
    {
        // 加锁 -- 防止重复创建抛异常
        std::lock_guard<std::mutex> _lock(_mutex);

        // 当前目录存在
        if (std::filesystem::exists(_logpath))
        {
            return;
        }

        // 不存在则创建目录
        try
        {
            std::filesystem::create_directories(_logpath);
        }
        catch (std::filesystem::filesystem_error &e)
        {
            std::cerr << e.what() << "\n";
        }
    }

    // 向磁盘文件输出
    void SyncLog(const std::string &message)
    {
        // 加锁 -- 防止乱序
        std::lock_guard<std::mutex> _lock(_mutex);

        // 以最加式打开文件
        std::string log = _logpath + _logname;
        std::ofstream _ofs(log, std::ios::app);

        if (!_ofs.is_open())
        {
            return;
        }

        _ofs << message << '\n';

        _ofs.close();
    }

    ~FileLogStrategy()
    {
    }

private:
    std::string _logpath;
    std::string _logname;

    std::mutex _mutex;
};

// 日志类 - 根据刷新策略进行刷新
class Logger
{
public:
    Logger()
    {
        // 默认向屏幕刷新
        _strategy = std::make_shared<ConsoleLogStrategy>();
    }

    // 设置向屏幕输出
    void EnableConsoleLog()
    {
        _strategy = std::make_shared<ConsoleLogStrategy>();
    }

    // 设置向文件输出
    void EnableFileLog()
    {
        _strategy = std::make_shared<FileLogStrategy>();
    }

    ~Logger() {}
    class LogMessage
    {
    public:
        LogMessage(LogLevel level, Logger &logger) : _logger(logger),
                                                     _level(level)
        {
        }
        LogMessage(LogLevel level, const std::string &filename, int line, Logger &logger)
            : _currtime(CurrentTime()),
              _level(level),
              _pid(getpid()),
              _filename(filename),
              _line(line),
              _logger(logger)
        {
            // 将数据构成一条完整的字符串
            std::stringstream ssbuff;
            ssbuff << "[" << _currtime << "]"
                   << "[" << Level2String(_level) << "]"
                   << "[" << _pid << "]"
                   << "[" << gettid() << "]"
                   << "[" << _filename << "]"
                   << "[" << _line << "]";

            _loginfo = ssbuff.str();
        }

        template <typename T>
        LogMessage &operator<<(const T &info)
        {
            // 添加字符串内容
            if (_level == LogLevel::DEBUG && DISABLEDEBUG)
            {
                return *this;
            }
            std::stringstream s;
            s << info;
            _loginfo += s.str();
            return *this;
        }

        ~LogMessage()
        {
            if (_loginfo.empty() == false && _logger._strategy != nullptr)
            {
                _logger._strategy->SyncLog(_loginfo);
            }
        }

    private:
        std::string _currtime; // 当前日志的时间
        LogLevel _level;       // 日志等级
        pid_t _pid;            // 进程pid
                               //  pid_t _tid;            // 当前线程id
        std::string _filename; // 源文件名称
        int _line;             // 日志所在的行号
        Logger &_logger;       // 负责根据不同的策略进行刷新
        std::string _loginfo;  // 一条完整的日志记录
    };

    // 就是要拷贝,故意的拷贝
    LogMessage operator()(LogLevel level, const std::string &filename, int line)
    {
        if (level == LogLevel::DEBUG && DISABLEDEBUG)
        {
            return LogMessage(level, *this);
        }

        return LogMessage(level, filename, line, *this);
    }

private:
    std::shared_ptr<LogStrategy> _strategy; // 日志刷新的策略方案
};

Logger logger;

#define LOG(Level) logger(Level, __FILE__, __LINE__)
#define ENABLE_CONSOLE_LOG() logger.EnableConsoleLog()
#define ENABLE_FILE_LOG() logger.EnableFileLog()
